'use client';

import './style.scss';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { useState, useCallback, useRef, FormEvent } from 'react';
import Loading from '@/app/component/Loading';
import { BoardLayout, PostConst } from '@/constants/forum';
import { fetchApi, throwError } from '@/lib/utils/client';
import { checkPermission, isBoardAdmin } from '@/lib/utils/permission';
import useAuth from '@/hooks/useAuth';
import useErrorAlert from '@/hooks/useErrorAlert';
import { BoardResponse } from '@/types/response/forum/board';
import { BoardListResponse } from '@/types/response/forum/board';
import { PostResponse } from '@/types/response/forum/post';
import Editor, { Handle } from '../../_component/Editor';
import HeaderContent from '../../_component/HeaderContent';
import FooterContent from '../../_component/FooterContent';
import PostTagInput from '../../_component/PostTagInput';

type Props = {
	_boardList: BoardListResponse,
	_board: BoardResponse,
	_post: PostResponse
};

export default function View({ _boardList, _board, _post }: Props)
{
	const router = useRouter();
	const { member } = useAuth();
	const { setError } = useErrorAlert();
	const [loading, setLoading] = useState<boolean>(false);
	const [isChanged, setIsChanged] = useState<boolean>(false);
	const [board, setBoard] = useState<BoardResponse|null>(_board);
	const [boardCode, setBoardCode] = useState<string>(_post.boardCode);
	const [boardPrefixID, setBoardPrefixID] = useState<string>(_post.boardPrefixID?.toString() ?? '');
	const [subject, setSubject] = useState<string>(_post.subject);
	const [content, setContent] = useState<string>(_post.content);
	const [isSecret, setIsSecret] = useState<boolean>(_post.isSecret);
	const [isNotice, setIsNotice] = useState<boolean>(_post.isNotice);
	const [isSpeaker, setIsSpeaker] = useState<boolean>(_post.isSpeaker);
	const [tags, setTags] = useState<string[]>(_post.tagList.map((tag) => tag.slug));

	const editorRef = useRef<Handle>(null);
	const boardCodeRef = useRef<HTMLSelectElement>(null);
	const boardPrefixIDRef = useRef<HTMLSelectElement>(null);
	const subjectRef = useRef<HTMLInputElement>(null);
	const contentRef = useRef<HTMLTextAreaElement>(null);
	const redirectUrl = `/post/${_post.id}`;

	// 게시글 초기화 (DB 데이터로 복원)
	const resetForm = () => {
		setIsChanged(false);
		setBoardPrefixID(_post.boardPrefixID?.toString() ?? '');
		setSubject(_post.subject);
		setIsSecret(_post.isSecret);
		setIsNotice(_post.isNotice);
		setIsSpeaker(_post.isSpeaker);
		setTags(_post.tagList.map((tag) => tag.slug));

		const originalContent = _post.content;
		setContent(originalContent);

		// Editor 초기화
		if (editorRef.current?.editorInstance) {
			editorRef.current.editorInstance.setData(originalContent);
		}
	};

	// 게시판 선택 시
	const handleBoardChange = useCallback((e: React.ChangeEvent<HTMLSelectElement>) => {
		const code = e.target.value;
		if (!code) {
			return;
		}

		if (isChanged) {
			if (!confirm('작성 중인 내용이 사라질 수 있습니다. 게시판을 변경하시겠습니까?')) {
				return
			}
		}

		setLoading(true);

		fetchApi<BoardResponse>('/api/forum/boards/' + code).then((res) => {
			if (res.success) {
				setBoardCode(code);
				setBoard(res.data);
				setIsChanged(false);
				resetForm();
			} else {
				throw new Error('게시판을 조회할 수 없습니다.');
			}
		}).catch((err) => {
			setError(err.message);
		}).finally(() => {
			setLoading(false);
		});
	}, [isChanged]);

	// 제목, 내용, 말머리 변경 시
	const handleChange = useCallback((e: React.ChangeEvent<HTMLSelectElement|HTMLInputElement|HTMLTextAreaElement>) => {
		const { name, value } = e.target as HTMLInputElement|HTMLSelectElement|HTMLTextAreaElement;
		const checked = (e.target as HTMLInputElement).checked;

		switch (name) {
			case 'boardPrefixID':
				setBoardPrefixID(value);
				break;
			case 'isSecret':
				setIsSecret(checked);
				break;
			case 'isNotice':
				setIsNotice(checked);
				setIsSpeaker(false);
				break;
			case 'isSpeaker':
				setIsSpeaker(checked);
				setIsNotice(false);
				break;
			case 'subject':
				setSubject(value);
				break;
			case 'content':
				setContent(value);
				break;
		}

		setIsChanged(true);
	}, []);

	// CKEditor에서 내용 변경 시
	const handleEditorChange = useCallback((data: string) => {
		setContent(data);
		setIsChanged(true);
	}, []);

	const validate = useCallback(() => {
		if (!boardCode || !board) {
			boardCodeRef.current?.focus();
			throw new Error('게시판을 선택해주세요.');
		}

		if (board.boardMeta.write.allowPrefix && board.boardMeta.write.requiredPrefix && !boardPrefixID) {
			boardPrefixIDRef.current?.focus();
			throw new Error((board.boardMeta.list.layout === BoardLayout.QnA ? '분류' : '말머리') + '를 선택해주세요.');
		}

		if (!subject) {
			subjectRef.current?.focus();
			throw new Error('제목을 입력해주세요.');
		} else if (subject.length > PostConst.MaxAllowedSubjectLength) {
			subjectRef.current?.focus();
			throw new Error(`제목은 ${PostConst.MaxAllowedSubjectLength}자 이내로 작성해주세요.`);
		}

		if (!content) {
			if (board.boardMeta.write.allowEditor) {
				editorRef.current?.editorInstance?.editing.view.focus();
			} else {
				contentRef.current?.focus();
			}

			throw new Error('내용을 입력해주세요.');
		} else if (!board.boardMeta.write.allowEditor) {
			// 기본 textarea 사용 시 글자 수 검사
			if (content.length > PostConst.MaxAllowedContentLength) {
				contentRef.current?.focus();
				throw new Error(`내용은 ${PostConst.MaxAllowedContentLength}자 이내로 작성해주세요.`);
			}
		}

		if (board.boardMeta.write.allowTag && tags.length > board.boardMeta.write.tagLimit) {
			throw new Error(`태그는 ${board.boardMeta.write.tagLimit}개 이내로 작성해주세요.`);
		}
	}, [boardCode, board, boardPrefixID, subject, content, tags]);

	// 게시글 수정 처리
	const handleSubmit = useCallback(async (e: FormEvent) => {
		e.preventDefault();

		try {

			validate();
			setLoading(true);

			if (!board) {
				throw new Error('게시판을 선택해 주세요.');
			}

			const formData = new FormData();
			formData.append('postID', _post.id.toString());
			formData.append('boardID', board.id.toString());
			formData.append('boardCode', boardCode);
			formData.append('boardPrefixID', boardPrefixID);
			formData.append('isSecret', isSecret.toString());
			formData.append('isNotice', isNotice.toString());
			formData.append('isSpeaker', isSpeaker.toString());
			formData.append('subject', subject);

			if (content) {
				const doc = new DOMParser().parseFromString(content, 'text/html');
				doc.querySelectorAll('img[src]').forEach(img => {
					const src = img.getAttribute('src');
					if (src && src.startsWith('data:image/')) {
						img.setAttribute('src', 'data:image/');
					}
				});

				formData.append('content', doc.body.innerHTML);
			}

			// 태그
			if (board.boardMeta.write.allowTag) {
				tags.forEach(tag => formData.append('tags', tag));
			}

			// 파일 업로드 권한 체크
			const canUploadFile = checkPermission(board.boardMeta, board.boardManager, member).canUploadFile;

			// 이미지 정보
			if (board.boardMeta.write.allowEditor && board.boardMeta.write.allowImage) {
				const images = editorRef.current?.getImageStore() || [];
				if (images.length > 0 && !canUploadFile) {
					throw new Error('이미지를 첨부할 수 있는 권한이 없습니다.');
				}
				images.forEach(i => {
					if (i.image?.size > 0 && i.name) {
						formData.append('images', i.image, i.name);
					}
				});
			}

			// 미디어 정보
			if (board.boardMeta.write.allowEditor && board.boardMeta.write.allowMedia) {
				const medias = editorRef.current?.getMediaStore() || [];
				if (medias.length > 0 && !canUploadFile) {
					throw new Error('영상을 첨부할 수 있는 권한이 없습니다.');
				}
				medias.forEach((m) => {
					if (m.url) {
						formData.append('medias', m.url);
					}
				});
			}

			// 첨부 파일
			if (board.boardMeta.write.allowEditor && board.boardMeta.write.allowFile) {
				const files = editorRef.current?.getFileStore() || [];
				if (files.length > 0 && !canUploadFile) {
					throw new Error('파일을 첨부할 수 있는 권한이 없습니다.');
				}
				files.forEach(f => {
					if (f?.size > 0 && f.name) {
						formData.append('files', f.file, f.name);
					}
				});
			}

			const res = await fetchApi('/api/forum/posts/' + _post.id, {
				method: 'PUT',
				body: formData
			});

			if (res.success) {
				resetForm();
				router.push(redirectUrl);
			} else {
				throwError(res);
			}

		} catch (err) {
			if (err instanceof Error) {
				setError(err.message);
			}
		}
	}, [boardCode, board, boardPrefixID, subject, content, isSecret, isNotice, isSpeaker, tags]);

	return (
		<form id='postEdit' onSubmit={handleSubmit}>
			{loading && <Loading />}

			<fieldset>
				<legend><h1>{board?.name} 글 수정</h1></legend>

				{/* 상단 안내 */}
				{<HeaderContent isEnabled={board?.boardMeta.write.showHeader} content={board?.boardMeta.write.headerContent}/>}

				{/* 게시판 선택, 말머리, 비밀글, 공지, 전체 공지 */}
				<section>

					{/* 게시판 선택 */}
					<article>
						<select name='boardCode' ref={boardCodeRef} value={boardCode} onChange={handleBoardChange} title='게시판 선택'>
							{_boardList.list.map((item) => (
								<option key={item.code} value={item.code}>{item.name}</option>
							))}
						</select>
					</article>

					{/* 말머리 */}
					{board?.boardMeta.write.allowPrefix && (
						<article>
							<select name='boardPrefixID' ref={boardPrefixIDRef} value={boardPrefixID} onChange={handleChange} title='말머리 선택'>
								<option value=''>{(board.boardMeta.list.layout === BoardLayout.QnA ? '분류' : '말머리') + ' 선택'}</option>
								{board.boardPrefix.map((row) => (
									<option key={row.id} value={row.id}>{row.name}</option>
								))}
							</select>
						</article>
					)}

					<article>
						{/* 비밀글 */}
						{board?.boardMeta.write.allowSecret && (
							<>
								<input type='checkbox' name='isSecret' id='isSecret' checked={isSecret} onChange={handleChange} />
								<label htmlFor='isSecret'>비밀글</label>
							</>
						)}

						{isBoardAdmin(board?.boardManager ?? [], member) && (
							<>
							{/* 해당 게시판 공지 */}
							{!board?.boardMeta.list.exceptNotice && (
								<>
								<input type='checkbox' name='isNotice' id='isNotice' checked={isNotice} onChange={handleChange} />
								<label htmlFor='isNotice'>공지</label>
								</>
							)}

							{/* 게시판 전체 공지 */}
							{!board?.boardMeta.list.exceptSpeaker && (
								<>
								<input type='checkbox' name='isSpeaker' id='isSpeaker' checked={isSpeaker} onChange={handleChange} />
								<label htmlFor='isSpeaker'>전체 공지</label>
								</>
							)}
							</>
						)}
					</article>
				</section>

				{/* 제목 */}
				<section>
					<input type='text' name='subject' ref={subjectRef} value={subject} onChange={handleChange} placeholder='글 제목을 입력해주세요.' autoFocus maxLength={PostConst.MaxAllowedSubjectLength} />
				</section>

				{/* 내용 */}
				<section>
					{board?.boardMeta.write.allowEditor ?
					(
						<Editor ref={editorRef} key={boardCode} editorKey={boardCode} data={content} onChange={handleEditorChange} boardMeta={board?.boardMeta} />
					) : (
						<textarea name='content' ref={contentRef} value={content} onChange={handleChange} placeholder='내용을 입력해주세요.' maxLength={PostConst.MaxAllowedContentLength}></textarea>
					)}
				</section>

				{/* 태그 */}
				{board?.boardMeta.write.allowTag && (
					<section id='postTag'>
						<PostTagInput value={tags} onChange={setTags} maxTags={board.boardMeta.write.tagLimit} />
					</section>
				)}

				{/* 하단 안내 */}
				{<FooterContent isEnabled={board?.boardMeta.write.showFooter} content={board?.boardMeta.write.footerContent}/>}

				<div>
					<br/>
				</div>

				<section>
					<button type='submit' className='btn btn-submit' disabled={loading}>
						{ loading ? '수정 중…' : '확인' }
					</button>
					<Link href={redirectUrl} className='btn btn-default'>취소</Link>
				</section>
			</fieldset>
		</form>
	);
}